Imports Glo = System.Globalization
Imports Iou = System.IO
Imports Ref = System.Reflection
Imports Xse = System.Xml.Serialization
Imports Sys = System
Imports Tex = System.Text
Imports W3x = System.Xml

Namespace Enhancements.Core.Logging
    ''' <summary>An XML style formatter. It implements full formatting features.</summary>
    Public NotInheritable Class XmlFormatter
        Inherits Formatter

        Private __StringBuilder As Tex.StringBuilder = New Tex.StringBuilder(Sys.String.Empty)
        Private __XmlDocument As W3x.XmlDocument = New W3x.XmlDocument()

        ''' <summary>Initializes a XmlFormatter instance.</summary>
        ''' <param name="_configuration">Configuration to use by the instance.</param>
        ''' <exception cref="IsDisposedException">The instance has been disposed.</exception>
        ''' <exception cref="IsNullException">_configuration is null.</exception>
        ''' <exception cref="IsOfTypeNotException">_configuration is not of type XmlFormatterConfiguration.</exception>
        Public Sub New(ByVal _configuration As FormatterConfiguration)
            Configuration = _configuration
        End Sub

        ''' <summary>Gets and sets the configuration used by the instance.</summary>
        ''' <exception cref="IsDisposedException">The instance has been disposed.</exception>
        ''' <exception cref="IsNullException">The value is null (while setting).</exception>
        ''' <exception cref="IsOfTypeNotException">The value is not of type XmlFormatterConfiguration (while setting).</exception>
        Public Overrides Property Configuration() As FormatterConfiguration
            Get
                ___Assert.___IsDisposedNot("XmlFormatter[instance]", ___DisposeCalled, True)

                Return CType(__Configuration, XmlFormatterConfiguration)
            End Get
            Set(ByVal _value As FormatterConfiguration)
                ___Assert.___IsDisposedNot("XmlFormatter[instance]", ___DisposeCalled, True)
                ___Assert.___IsNullNot("Configuration._value", _value, True)
                ___Assert.___IsOfType("Configuration._value", _value, GetType(XmlFormatterConfiguration), True)

                __Configuration = _value
            End Set
        End Property

        Friend Overrides Sub ___Format(ByVal _record As ___Record)
            ___Assert.___IsDisposedNot("XmlFormatter[instance]", ___DisposeCalled, True)
            ___Assert.___IsNullNot("_record", _record, True)
            ___Assert.___IsEmptyNot("_record.___UnformattedIdentifier", _record.___UnformattedIdentifier, True)
            ___Assert.___IsNullNot("_record.___UnformattedCallingProcedure", _record.___UnformattedCallingProcedure, True)
            ___Assert.___IsNullNot("_record.___UnformattedObjects", _record.___UnformattedObjects, True)

            __StringBuilder.Remove(0, __StringBuilder.Length)

            __StringBuilderAppend("<Record>", 0, True)

            __StringBuilderAppend("<Identifier>", 1, False)
            __StringBuilderAppend(_record.___UnformattedIdentifier.ToString("N"), 0, False)
            __StringBuilderAppend("</Identifier>", True)

            __StringBuilderAppend("<Moment>", 1, True)
            __StringBuilderAppend("<Date>", 2, False)
            __StringBuilderAppend(_record.___UnformattedMoment.ToShortDateString(), 0, False)
            __StringBuilderAppend("</Date>", True)
            __StringBuilderAppend("<Time>", 2, False)
            __StringBuilderAppend(_record.___UnformattedMoment.TimeOfDay.ToString(), 0, False)
            __StringBuilderAppend("</Time>", True)
            __StringBuilderAppend("</Moment>", 1, True)

            __StringBuilderAppend("<Level>", 1, False)
            __StringBuilderAppend(_record.___UnformattedLevel.ToString(), 0, False)
            __StringBuilderAppend("</Level>", True)

            __StringBuilderAppend("<Method>", 1, True)
            __StringBuilderAppend("<Type>", 2, False)
            __StringBuilderAppend(_record.___UnformattedCallingProcedure.DeclaringType.FullName, 0, False)
            __StringBuilderAppend("</Type>", True)
            __StringBuilderAppend("<Name>", 2, False)
            __StringBuilderAppend(_record.___UnformattedCallingProcedure.Name, 0, False)
            __StringBuilderAppend("</Name>", True)
            __StringBuilderAppend("</Method>", 1, True)

            __StringBuilderAppend("<ProcedureExecutionMoment>", 1, False)
            __StringBuilderAppend(_record.___UnformattedProcedureExecutionMoment.ToString(), 0, False)
            __StringBuilderAppend("</ProcedureExecutionMoment>", True)

            If ___Assert.___IsNull("_record.___UnformattedCallingObject", _record.___UnformattedCallingObject, False) Then
                __StringBuilderAppend("<CallingObject null='true'/>", 1, True)
            Else
                __StringBuilderAppend("<CallingObject>", 1, True)
                __StringBuilderAppend("<Type>", 2, False)
                __StringBuilderAppend(_record.___UnformattedCallingObject.GetType().FullName, 0, False)
                __StringBuilderAppend("</Type>", True)
                __StringBuilderAppend("<Value>", 2, True)
                __StringBuilderAppend("<![CDATA[", 3, False)
                __StringBuilderAppend(__Serialize(_record.___UnformattedCallingObject), False)
                __StringBuilderAppend("]]>", True)
                __StringBuilderAppend("</Value>", 2, True)
                __StringBuilderAppend("</CallingObject>", 1, True)
            End If

            __StringBuilderAppend("<Objects>", 1, True)
            For _counter As Sys.Int32 = 0 To _record.___UnformattedObjects.Length - 1
                Dim _object As Sys.Object
                _object = _record.___UnformattedObjects(_counter)
                If ___Assert.___IsNull("_object", _object, False) Then
                    __StringBuilderAppend("<Object null='true'/>", 2, True)
                Else
                    __StringBuilderAppend("<Object>", 2, True)
                    __StringBuilderAppend("<Type>", 3, False)
                    __StringBuilderAppend(_object.GetType().FullName, 0, False)
                    __StringBuilderAppend("</Type>", True)
                    __StringBuilderAppend("<Value>", 3, True)
                    __StringBuilderAppend("<![CDATA[", 4, False)
                    __StringBuilderAppend(__Serialize(_object), False)
                    __StringBuilderAppend("]]>", True)
                    __StringBuilderAppend("</Value>", 3, True)
                    __StringBuilderAppend("</Object>", 2, True)
                End If
            Next _counter
            __StringBuilderAppend("</Objects>", 1, True)

            __StringBuilderAppend("</Record>", 0, True)

            _record.___Formatted = CType(Configuration, XmlFormatterConfiguration).Encoding.GetBytes(__StringBuilder.ToString())
        End Sub

        Private Sub __StringBuilderAppend(ByVal _value As System.String, ByVal _includeCrlf As System.Boolean)
            __StringBuilder.Append(_value)
            If ___Assert.___IsTrue("_includeCrlf", _includeCrlf, False) Then
                __StringBuilder.Append(vbCrLf)
            End If
        End Sub

        Private Sub __StringBuilderAppend(ByVal _value As System.String, ByVal _valueNesting As Sys.Int32, ByVal _includeCrlf As System.Boolean)
            Dim _configuration As XmlFormatterConfiguration = CType(Configuration, XmlFormatterConfiguration)

            If ___Assert.___IsTrue("_configuration.IndentationUse", _configuration.IndentationUse, False) Then
                __StringBuilder.Append(_configuration.IndentationCharacter, _configuration.IndentationLength * _valueNesting)
            End If
            __StringBuilder.Append(_value)
            If ___Assert.___IsTrue("_includeCrlf", _includeCrlf, False) Then
                __StringBuilder.Append(vbCrLf)
            End If
        End Sub

        Private Function __Serialize(ByVal _object As Sys.Object) As System.String
            Dim _returnValue As System.String = Sys.String.Empty

            Dim _stream As Iou.MemoryStream = Nothing
            Dim _serializer As Xse.XmlSerializer = Nothing

            Try
                _stream = New Iou.MemoryStream()
                _serializer = New Xse.XmlSerializer(_object.GetType())
                _serializer.Serialize(_stream, _object)
                _stream.Seek(0, Iou.SeekOrigin.Begin)
                __XmlDocument.Load(_stream)

                _returnValue = __XmlDocument.DocumentElement.InnerXml
            Catch _exception As Exception
                _returnValue = "Serialization exception: " + _exception.Message
            End Try

            If _stream IsNot Nothing Then
                Try
                    _stream.Flush()
                Catch
                End Try
                Try
                    _stream.Dispose()
                Catch
                End Try
            End If

            Return _returnValue
        End Function

        Friend Overrides Sub ___Dispose()
            ___Assert.___IsDisposedNot("XmlFormatter[instance]", ___DisposeCalled, True)
            ___DisposeCalled = True

            __StringBuilder.Remove(0, __StringBuilder.Length)
        End Sub

    End Class
End Namespace
